import _defineProperty from 'babel-runtime/helpers/defineProperty';
import _typeof from 'babel-runtime/helpers/typeof';
import _classCallCheck from 'babel-runtime/helpers/classCallCheck';
import _possibleConstructorReturn from 'babel-runtime/helpers/possibleConstructorReturn';
import _inherits from 'babel-runtime/helpers/inherits';
import React from 'react';
import ReactDOM from 'react-dom';
import PropTypes from 'prop-types';
import classnames from 'classnames';
import ContainerRender from '../util/ContainerRender';
import getScrollBarSize from '../util/getScrollBarSize';
import { dataToArray, transitionEnd, trnasitionStr, addEventListener, removeEventListener, transformArguments, isNumeric } from './utils';

var IS_REACT_16 = 'createPortal' in ReactDOM;

var currentDrawer = {};
var windowIsUndefined = typeof window === 'undefined';

var Drawer = function (_React$PureComponent) {
  _inherits(Drawer, _React$PureComponent);

  function Drawer(props) {
    _classCallCheck(this, Drawer);

    var _this = _possibleConstructorReturn(this, _React$PureComponent.call(this, props));

    _initialiseProps.call(_this);

    _this.levelDom = [];
    _this.contentDom = null;
    _this.maskDom = null;
    _this.handlerdom = null;
    _this.mousePos = null;
    _this.firstEnter = props.firstEnter; // 记录首次进入.
    _this.timeout = null;
    _this.drawerId = Number((Date.now() + Math.random()).toString().replace('.', Math.round(Math.random() * 9))).toString(16);
    var open = props.open !== undefined ? props.open : !!props.defaultOpen;
    currentDrawer[_this.drawerId] = open;
    _this.state = {
      open: open
    };
    return _this;
  }

  Drawer.prototype.componentDidMount = function componentDidMount() {
    if (!windowIsUndefined) {
      var passiveSupported = false;
      window.addEventListener('test', null, Object.defineProperty({}, 'passive', {
        get: function get() {
          passiveSupported = true;
          return null;
        }
      }));
      this.passive = passiveSupported ? { passive: false } : false;
    }
    var open = this.getOpen();
    if (this.props.handler || open || this.firstEnter) {
      this.getDefault(this.props);
      if (open) {
        this.isOpenChange = true;
      }
      this.forceUpdate();
    }
  };

  Drawer.prototype.componentWillReceiveProps = function componentWillReceiveProps(nextProps) {
    var open = nextProps.open,
        placement = nextProps.placement;

    if (open !== undefined && open !== this.props.open) {
      this.isOpenChange = true;
      // 没渲染 dom 时，获取默认数据;
      if (!this.container) {
        this.getDefault(nextProps);
      }
      this.setState({
        open: open
      });
    }
    if (placement !== this.props.placement) {
      // test 的 bug, 有动画过场，删除 dom
      this.contentDom = null;
    }
    if (this.props.level !== nextProps.level) {
      this.getParentAndLevelDom(nextProps);
    }
  };

  Drawer.prototype.componentDidUpdate = function componentDidUpdate() {
    // dom 没渲染时，重走一遍。
    if (!this.firstEnter && this.container) {
      this.forceUpdate();
      this.firstEnter = true;
    }
  };

  Drawer.prototype.componentWillUnmount = function componentWillUnmount() {
    delete currentDrawer[this.drawerId];
    delete this.isOpenChange;
    if (this.container) {
      if (this.state.open) {
        this.setLevelDomTransform(false, true);
      }
      document.body.style.overflow = '';
      // 拦不住。。直接删除；
      if (this.props.getContainer) {
        this.container.parentNode.removeChild(this.container);
      }
    }
    this.firstEnter = false;
    clearTimeout(this.timeout);
    // suppport react15
    // 需要 didmount 后也会渲染，直接 unmount 将不会渲染，加上判断.
    if (this.renderComponent && !IS_REACT_16) {
      this.renderComponent({
        afterClose: this.removeContainer,
        onClose: function onClose() {},

        visible: false
      });
    }
  };

  Drawer.prototype.render = function render() {
    var _this2 = this;

    var _props = this.props,
        getContainer = _props.getContainer,
        wrapperClassName = _props.wrapperClassName;

    var open = this.getOpen();
    currentDrawer[this.drawerId] = open ? this.container : open;
    var children = this.getChildToRender(this.firstEnter ? open : false);
    if (!getContainer) {
      return React.createElement(
        'div',
        {
          className: wrapperClassName,
          ref: function ref(c) {
            _this2.container = c;
          }
        },
        children
      );
    }
    if (!this.container || !open && !this.firstEnter) {
      return null;
    }
    // suppport react15
    if (!IS_REACT_16) {
      return React.createElement(
        ContainerRender,
        {
          parent: this,
          visible: true,
          autoMount: true,
          autoDestroy: false,
          getComponent: function getComponent() {
            return children;
          },
          getContainer: this.getContainer
        },
        function (_ref) {
          var renderComponent = _ref.renderComponent,
              removeContainer = _ref.removeContainer;

          _this2.renderComponent = renderComponent;
          _this2.removeContainer = removeContainer;
          return null;
        }
      );
    }
    return ReactDOM.createPortal(children, this.container);
  };

  return Drawer;
}(React.PureComponent);

Drawer.defaultProps = {
  prefixCls: 'drawer',
  placement: 'left',
  getContainer: 'body',
  level: 'all',
  duration: '.3s',
  ease: 'cubic-bezier(0.78, 0.14, 0.15, 0.86)',
  onChange: function onChange() {},
  onMaskClick: function onMaskClick() {},
  onHandleClick: function onHandleClick() {},
  handler: React.createElement(
    'div',
    { className: 'drawer-handle' },
    React.createElement('i', { className: 'drawer-handle-icon' })
  ),
  firstEnter: false,
  showMask: true,
  maskStyle: {},
  wrapperClassName: '',
  className: ''
};

var _initialiseProps = function _initialiseProps() {
  var _this3 = this;

  this.onMaskTouchEnd = function (e) {
    _this3.props.onMaskClick(e);
    _this3.onTouchEnd(e, true);
  };

  this.onIconTouchEnd = function (e) {
    _this3.props.onHandleClick(e);
    _this3.onTouchEnd(e);
  };

  this.onTouchEnd = function (e, close) {
    if (_this3.props.open !== undefined) {
      return;
    }
    var open = close || _this3.state.open;
    _this3.isOpenChange = true;
    _this3.setState({
      open: !open
    });
  };

  this.onWrapperTransitionEnd = function (e) {
    if (e.target === _this3.contentWrapper) {
      _this3.dom.style.transition = '';
      if (!_this3.state.open && _this3.getCrrentDrawerSome()) {
        document.body.style.overflowX = '';
        if (_this3.maskDom) {
          _this3.maskDom.style.left = '';
          _this3.maskDom.style.width = '';
        }
      }
    }
  };

  this.getDefault = function (props) {
    _this3.getParentAndLevelDom(props);
    if (props.getContainer || props.parent) {
      _this3.container = _this3.defaultGetContainer();
    }
  };

  this.getCrrentDrawerSome = function () {
    return !Object.keys(currentDrawer).some(function (key) {
      return currentDrawer[key];
    });
  };

  this.getContainer = function () {
    return _this3.container;
  };

  this.getParentAndLevelDom = function (props) {
    if (windowIsUndefined) {
      return;
    }
    var level = props.level,
        getContainer = props.getContainer;

    _this3.levelDom = [];
    if (getContainer) {
      if (typeof getContainer === 'string') {
        var dom = document.querySelectorAll(getContainer)[0];
        _this3.parent = dom;
      }
      if (typeof getContainer === 'function') {
        _this3.parent = getContainer();
      }
      if ((typeof getContainer === 'undefined' ? 'undefined' : _typeof(getContainer)) === 'object' && getContainer instanceof window.HTMLElement) {
        _this3.parent = getContainer;
      }
    }
    if (!getContainer && _this3.container) {
      _this3.parent = _this3.container.parentNode;
    }
    if (level === 'all') {
      var children = Array.prototype.slice.call(_this3.parent.children);
      children.forEach(function (child) {
        if (child.nodeName !== 'SCRIPT' && child.nodeName !== 'STYLE' && child.nodeName !== 'LINK' && child !== _this3.container) {
          _this3.levelDom.push(child);
        }
      });
    } else if (level) {
      dataToArray(level).forEach(function (key) {
        document.querySelectorAll(key).forEach(function (item) {
          _this3.levelDom.push(item);
        });
      });
    }
  };

  this.setLevelDomTransform = function (open, openTransition, placementName, value) {
    var _props2 = _this3.props,
        placement = _props2.placement,
        levelMove = _props2.levelMove,
        duration = _props2.duration,
        ease = _props2.ease,
        onChange = _props2.onChange;

    if (!windowIsUndefined) {
      _this3.levelDom.forEach(function (dom) {
        if (_this3.isOpenChange || openTransition) {
          /* eslint no-param-reassign: "error" */
          dom.style.transition = 'transform ' + duration + ' ' + ease;
          addEventListener(dom, transitionEnd, _this3.trnasitionEnd);
          var levelValue = open ? value : 0;
          if (levelMove) {
            var $levelMove = transformArguments(levelMove, { target: dom, open: open });
            levelValue = open ? $levelMove[0] : $levelMove[1] || 0;
          }
          var $value = typeof levelValue === 'number' ? levelValue + 'px' : levelValue;
          var placementPos = placement === 'left' || placement === 'top' ? $value : '-' + $value;
          dom.style.transform = levelValue ? placementName + '(' + placementPos + ')' : '';
          dom.style.msTransform = levelValue ? placementName + '(' + placementPos + ')' : '';
        }
      });
      // 处理 body 滚动
      var eventArray = ['touchstart'];
      var domArray = [document.body, _this3.maskDom, _this3.handlerdom, _this3.contentDom];
      var right = getScrollBarSize(1);
      var widthTransition = 'width ' + duration + ' ' + ease;
      var trannsformTransition = 'transform ' + duration + ' ' + ease;
      if (open && document.body.style.overflow !== 'hidden') {
        document.body.style.overflow = 'hidden';
        if (right) {
          document.body.style.position = 'relative';
          document.body.style.width = 'calc(100% - ' + right + 'px)';
          _this3.dom.style.transition = 'none';
          switch (placement) {
            case 'right':
              _this3.dom.style.transform = 'translateX(-' + right + 'px)';
              _this3.dom.style.msTransform = 'translateX(-' + right + 'px)';
              break;
            case 'top':
            case 'bottom':
              _this3.dom.style.width = 'calc(100% - ' + right + 'px)';
              _this3.dom.style.transform = 'translateZ(0)';
              break;
            default:
              break;
          }
          clearTimeout(_this3.timeout);
          _this3.timeout = setTimeout(function () {
            _this3.dom.style.transition = trannsformTransition + ',' + widthTransition;
            _this3.dom.style.width = '';
            _this3.dom.style.transform = '';
            _this3.dom.style.msTransform = '';
          });
        }
        // 手机禁滚
        domArray.forEach(function (item, i) {
          if (!item) {
            return;
          }
          addEventListener(item, eventArray[i] || 'touchmove', i ? _this3.removeMoveHandler : _this3.removeStartHandler, _this3.passive);
        });
      } else if (_this3.getCrrentDrawerSome()) {
        document.body.style.overflow = '';
        if ((_this3.isOpenChange || openTransition) && right) {
          document.body.style.position = '';
          document.body.style.width = '';
          if (trnasitionStr) {
            document.body.style.overflowX = 'hidden';
          }
          _this3.dom.style.transition = 'none';
          var heightTransition = void 0;
          switch (placement) {
            case 'right':
              {
                _this3.dom.style.transform = 'translateX(' + right + 'px)';
                _this3.dom.style.msTransform = 'translateX(' + right + 'px)';
                _this3.dom.style.width = '100%';
                widthTransition = 'width 0s ' + ease + ' ' + duration;
                if (_this3.maskDom) {
                  _this3.maskDom.style.left = '-' + right + 'px';
                  _this3.maskDom.style.width = 'calc(100% + ' + right + 'px)';
                }
                break;
              }
            case 'top':
            case 'bottom':
              {
                _this3.dom.style.width = 'calc(100% + ' + right + 'px)';
                _this3.dom.style.height = '100%';
                _this3.dom.style.transform = 'translateZ(0)';
                heightTransition = 'height 0s ' + ease + ' ' + duration;
                break;
              }
            default:
              break;
          }
          clearTimeout(_this3.timeout);
          _this3.timeout = setTimeout(function () {
            _this3.dom.style.transition = trannsformTransition + ',' + (heightTransition ? heightTransition + ',' : '') + widthTransition;
            _this3.dom.style.transform = '';
            _this3.dom.style.msTransform = '';
            _this3.dom.style.width = '';
            _this3.dom.style.height = '';
          });
        }
        domArray.forEach(function (item, i) {
          if (!item) {
            return;
          }
          removeEventListener(item, eventArray[i] || 'touchmove', i ? _this3.removeMoveHandler : _this3.removeStartHandler, _this3.passive);
        });
      }
    }

    if (onChange && _this3.isOpenChange && _this3.firstEnter) {
      onChange(open);
      _this3.isOpenChange = false;
    }
  };

  this.getChildToRender = function (open) {
    var _classnames;

    var _props3 = _this3.props,
        className = _props3.className,
        prefixCls = _props3.prefixCls,
        style = _props3.style,
        placement = _props3.placement,
        children = _props3.children,
        handler = _props3.handler,
        showMask = _props3.showMask,
        maskStyle = _props3.maskStyle,
        width = _props3.width,
        height = _props3.height;

    var wrapperClassname = classnames(prefixCls, (_classnames = {}, _defineProperty(_classnames, prefixCls + '-' + placement, true), _defineProperty(_classnames, prefixCls + '-open', open), _defineProperty(_classnames, className, !!className), _classnames));
    var isOpenChange = _this3.isOpenChange;
    var isHorizontal = placement === 'left' || placement === 'right';
    var placementName = 'translate' + (isHorizontal ? 'X' : 'Y');
    // 百分比与像素动画不同步，第一次打用后全用像素动画。
    // const defaultValue = !this.contentDom || !level ? '100%' : `${value}px`;
    var placementPos = placement === 'left' || placement === 'top' ? '-100%' : '100%';
    var transform = open ? '' : placementName + '(' + placementPos + ')';
    if (isOpenChange === undefined || isOpenChange) {
      var contentValue = _this3.contentDom ? _this3.contentDom.getBoundingClientRect()[isHorizontal ? 'width' : 'height'] : 0;
      var value = (isHorizontal ? width : height) || contentValue;
      _this3.setLevelDomTransform(open, false, placementName, value);
    }
    var handlerCildren = handler && React.cloneElement(handler, {
      onClick: function onClick(e) {
        if (handler.props.onClick) {
          handler.props.onClick();
        }
        _this3.onIconTouchEnd(e);
      },
      ref: function ref(c) {
        _this3.handlerdom = c;
      }
    });
    return React.createElement(
      'div',
      {
        className: wrapperClassname,
        style: style,
        ref: function ref(c) {
          _this3.dom = c;
        },
        onTransitionEnd: _this3.onWrapperTransitionEnd
      },
      showMask && React.createElement('div', {
        className: prefixCls + '-mask',
        onClick: _this3.onMaskTouchEnd,
        style: maskStyle,
        ref: function ref(c) {
          _this3.maskDom = c;
        }
      }),
      React.createElement(
        'div',
        {
          className: prefixCls + '-content-wrapper',
          style: {
            transform: transform,
            msTransform: transform,
            width: isNumeric(width) ? width + 'px' : width,
            height: isNumeric(height) ? height + 'px' : height
          },
          ref: function ref(c) {
            _this3.contentWrapper = c;
          }
        },
        React.createElement(
          'div',
          {
            className: prefixCls + '-content',
            ref: function ref(c) {
              _this3.contentDom = c;
            },
            onTouchStart: open ? _this3.removeStartHandler : null // 跑用例用
            , onTouchMove: open ? _this3.removeMoveHandler : null // 跑用例用
          },
          children
        ),
        handlerCildren
      )
    );
  };

  this.getOpen = function () {
    return _this3.props.open !== undefined ? _this3.props.open : _this3.state.open;
  };

  this.getTouchParentScroll = function (root, currentTarget, differX, differY) {
    /**
     * 增加 rect。
     * 当父级 dom 的 overflow 未开启滚动时，scrollLeft 或 scrollTop 为 0, 而 scrollWidth 增加了，
     * 父级是跟随子级的 rect, 直到父级设定了滚动.
     */
    var rect = currentTarget.getBoundingClientRect();
    if (!currentTarget) {
      return false;
    } else if ((currentTarget.scrollTop + currentTarget.offsetHeight + currentTarget.offsetTop >= currentTarget.scrollHeight + rect.top && differY < 0 || currentTarget.scrollTop <= 0 && differY > 0) && Math.max(Math.abs(differX), Math.abs(differY)) === Math.abs(differY) || (currentTarget.scrollLeft + currentTarget.offsetWidth + currentTarget.offsetLeft >= currentTarget.scrollWidth + rect.left && differX < 0 || currentTarget.scrollLeft <= 0 && differX > 0) && Math.max(Math.abs(differX), Math.abs(differY)) === Math.abs(differX)) {
      return root === currentTarget || _this3.getTouchParentScroll(root, currentTarget.parentNode, differX, differY);
    }
    return false;
  };

  this.removeStartHandler = function (e) {
    if (e.touches.length > 1) {
      return;
    }
    _this3.startPos = {
      x: e.touches[0].clientX,
      y: e.touches[0].clientY
    };
  };

  this.removeMoveHandler = function (e) {
    if (e.changedTouches.length > 1) {
      return;
    }
    var currentTarget = e.currentTarget;
    var differX = e.changedTouches[0].clientX - _this3.startPos.x;
    var differY = e.changedTouches[0].clientY - _this3.startPos.y;
    if (currentTarget === _this3.maskDom || currentTarget === _this3.handlerdom || currentTarget === _this3.contentDom && _this3.getTouchParentScroll(currentTarget, e.target, differX, differY)) {
      e.preventDefault();
    }
  };

  this.trnasitionEnd = function (e) {
    removeEventListener(e.target, transitionEnd, _this3.trnasitionEnd);
    e.target.style.transition = '';
  };

  this.defaultGetContainer = function () {
    if (windowIsUndefined) {
      return null;
    }
    var container = document.createElement('div');
    _this3.parent.appendChild(container);
    if (_this3.props.wrapperClassName) {
      container.className = _this3.props.wrapperClassName;
    }
    return container;
  };
};

Drawer.propTypes = {
  wrapperClassName: PropTypes.string,
  className: PropTypes.string,
  children: PropTypes.node,
  style: PropTypes.object,
  width: PropTypes.any,
  height: PropTypes.any,
  defaultOpen: PropTypes.bool,
  firstEnter: PropTypes.bool,
  open: PropTypes.bool,
  prefixCls: PropTypes.string,
  placement: PropTypes.string,
  level: PropTypes.oneOfType([PropTypes.string, PropTypes.array]),
  levelMove: PropTypes.oneOfType([PropTypes.number, PropTypes.func, PropTypes.array]),
  ease: PropTypes.string,
  duration: PropTypes.string,
  getContainer: PropTypes.oneOfType([PropTypes.string, PropTypes.func, PropTypes.object, PropTypes.bool]),
  handler: PropTypes.any,
  onChange: PropTypes.func,
  onMaskClick: PropTypes.func,
  onHandleClick: PropTypes.func,
  showMask: PropTypes.bool,
  maskStyle: PropTypes.object
};

export default Drawer;